#ifndef PATTERN_H
#define PATTERN_H
#include "AutoPointer.h"
#include "CopyPointer.h"
#include "ReferenceCountingPointer.h"
#include <string>
#include <sstream>
#include <boost/shared_ptr.hpp>


namespace TreeExpression {
class Expression;
}

class Context;

namespace PatternRecognition {

enum TypeWeight { ANY = 1, TYPE };

/*template<typename T>
class ScopedPointer {
	public:
		explicit ScopedPointer(T * p = 0): ptr(p) { }

		~ScopedPointer() {
			delete ptr;
		}

		void reset(T * p = 0) {
			ptr = p;
		}

		T & operator*() const { return *ptr; }

		T * operator->() const { return ptr; }

		T * get() const { return ptr; }

		operator bool () const { return ptr != 0; }

	private:
		ScopedPointer(ScopedPointer const &) { }
		ScopedPointer & operator=(ScopedPointer const &) { }

		T *ptr;
};*/

class AbstractSinglePattern {
public:
	AbstractSinglePattern() {};
	virtual ~AbstractSinglePattern() {};
	virtual void set(const AutoPointer<TreeExpression::Expression>&, CopyPointer<Context>&) const { }
	virtual int match(const AutoPointer<TreeExpression::Expression>&) { return 0; } //TODO Pure virtual
	virtual void setName(const std::string&) {  }
	virtual std::string getName() const { return ""; }
	virtual std::string getHash() const { return ""; }
	virtual std::string toString() const { return ""; }
};

template<typename T>
class SinglePattern : public AbstractSinglePattern {
public:
	explicit SinglePattern(T const& _prs);

	virtual void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	virtual int match(const AutoPointer<TreeExpression::Expression>& expr);
	virtual void setName(const std::string&);
	virtual std::string getName() const;
	virtual std::string getHash() const;
	virtual std::string toString() const;
private:
	T prs;
};

template<typename T = AbstractSinglePattern>
class Pattern {
public:
	Pattern() { }
	~Pattern() { /*delete pattern;*/ }//TODO pattern ptr with reference counting

	template<typename U> explicit Pattern(U const& r) : pattern(new SinglePattern<U>(r)) { }

	template<typename U> explicit Pattern(U* r) : pattern(r) { }

	//inline T* get() { return pattern; }

	//Pattern(Pattern const& r) { pattern = new SinglePattern<T>(r); }

	template<typename U> Pattern& operator=(U const& r) { pattern = new SinglePattern<U>(r); return *this; }

	void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	int match(const AutoPointer<TreeExpression::Expression>& expr);
	void setName(const std::string&);
	std::string getName() const;
	std::string getHash() const;
	std::string toString() const;

	typedef Pattern<T> const& embed_t;
private:
	boost::shared_ptr<T> pattern;
};

template<typename T, typename V> class SequencePattern {
public:
	SequencePattern(const T& _a, const V& _b) : a(_a), b(_b) { }

	void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	int match(const AutoPointer<TreeExpression::Expression>& expr);

	typedef SequencePattern<T, V> embed_t;

	void setName(const std::string& _name) { name = _name; }
	std::string getName() const { return name; }
	std::string getHash() const {
		std::ostringstream oss;
		oss << "sequence " << a.getHash() << " " << b.getHash();
		return oss.str();
	}
	std::string toString() const {
		std::ostringstream oss;
		oss << "sequence " << a.toString() << " " << b.toString() << " " << name;
		return oss.str();
	}
private:
	T a;
	V b;
	std::string name;
};

template<typename T, typename V> class NodePattern {
public:
	NodePattern(const T& _node, const V& _son) : node(_node), son(_son) { }

	void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	int match(const AutoPointer<TreeExpression::Expression>& expr);

	typedef NodePattern<T, V> embed_t;

	void setName(const std::string& _name) { name = _name; }
	std::string getName() const { return name; }
	std::string getHash() const {
		std::ostringstream oss;
		oss << "node " << node.getHash() << " (" << son.getHash() << ") ";
		return oss.str();
	}
	std::string toString() const {
		std::ostringstream oss;
		oss << node.toString() << " {" << son.toString() << "} " << name;
		return oss.str();
	}
private:
	T node;
	V son;
	std::string name;
};

class AnythingPattern {
public:
	AnythingPattern(AutoPointer<std::string>* _signatureHash, const std::string& _name) : signatureHash(_signatureHash), name(_name) { }
	~AnythingPattern() { delete signatureHash; }

	void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	int match(const AutoPointer<TreeExpression::Expression>& expr);

	void setName(const std::string& _name) { name = _name; }
	std::string getName() const { return name; }
	std::string getHash() const {
		std::ostringstream oss;
		oss << "any";
		return oss.str();
	}
	std::string toString() const {
		std::ostringstream oss;
		oss << "any " << name;
		return oss.str();
	}
private:
	AutoPointer<std::string>* signatureHash;
	std::string name;
};

class TypePattern {
public:
	TypePattern(const std::string& _signatureHash, const std::string& _name) : signatureHash(_signatureHash), name(_name) { }

	void set(const AutoPointer<TreeExpression::Expression>& expr, CopyPointer<Context>& context) const;
	int match(const AutoPointer<TreeExpression::Expression>& expr);

	void setName(const std::string& _name) { name = _name; }
	std::string getName() const { return name; }
	std::string getHash() const {
		return signatureHash;
	}
	std::string toString() const {
		std::ostringstream oss;
		oss << signatureHash << " " << name;
		return oss.str();
	}
private:
	std::string signatureHash;
	std::string name;
};

//TODO set, get, memory

template<class T, class U> SequencePattern<T,U> operator,(const T& a, const U& b) {
	return SequencePattern<T,U>(a,b);
}

template<class T, class U> NodePattern<T,U> operator<<(const T& a, const U& b) {
	return NodePattern<T,U>(a,b);
}

}

using PatternRecognition::Pattern;
using PatternRecognition::TypePattern;
using PatternRecognition::NodePattern;
using PatternRecognition::SequencePattern;
using PatternRecognition::AnythingPattern;

#endif
